﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;

namespace Fin
{
    public class XML
    {
        private XmlElement root = null;

        public XmlElement Root
        {
            get { return root; }
        }
        private string path;
        /// <summary>
        /// If file does not exist or root can not find => default root: "Root"
        /// </summary>
        /// <param name="path"></param>
        public XML(string path)
        {            
            Console.WriteLine("XML(string path)");
            this.path = path;
            //check file exist
            if (!File.Exists(path))
            {
                createFile(path);                 
                XmlDocument document = new XmlDocument();
                this.root = document.CreateElement("Root"); //default root name : "Root"
                document.AppendChild(this.root);
                document.Save(path);
            }
            else
            {
                 
                XmlDocument document = new XmlDocument();
                try
                {
                    document.Load(path);                    
                    this.root = document.DocumentElement;
                }
                catch(Exception e)
                {
                    Console.WriteLine(e.Message);
                }
                finally
                {
                    if (this.root == null)
                    {
                        this.root = document.CreateElement("Root"); //default root name : "Root"
                        document.AppendChild(this.root);
                        document.Save(path);
                    }
                }
                
            }
        }
       
        private void createFile(string path)
        {
            try
            {
                File.Create(path);          
            }
            catch (IOException ioe)
            {
                Console.WriteLine("- Can not create file at: " + path);
                Console.WriteLine("\t System message: " + ioe.Message);
            }
        }

        public void ChangeRootName(string newName)
        {    
            //new document
            XmlDocument oldDoc = this.root.OwnerDocument;            
            XmlDocument newDoc = new XmlDocument();
            XmlNodeList oldNodes = oldDoc.SelectNodes(root.Name);
            for (int i = 0; i < oldNodes.Count; i++)
            {
                XmlElement newNode = newDoc.CreateElement(newName);
                newNode.InnerXml = oldNodes[i].InnerXml;
                newDoc.AppendChild(newNode);
            }
            this.root = newDoc.DocumentElement;
            newDoc.Save(path);            
        }
        public void ChangeNodeName(string oldName, string newName)
        {
            XmlDocument oldDoc = this.root.OwnerDocument;
            XmlNodeList oldNodes = oldDoc.SelectNodes("//"+oldName);
            for (int i = 0; i < oldNodes.Count; i++)
            {
                XmlElement newNode = oldDoc.CreateElement(newName);
                XmlNode parent = oldNodes[i].ParentNode;
                newNode.InnerXml = oldNodes[i].InnerXml;
                parent.RemoveChild(oldNodes[i]);
                parent.AppendChild(newNode);
            }
            oldDoc.Save(path);  
        }
        /// <summary>
        /// Create xml file with path. Xml file have root name : root
        /// </summary>
        /// <param name="path"></param>
        /// <param name="root"></param>
        public XML(string path, string root) 
            :this(path)
        {
            Console.WriteLine("XML(string path, string root)");
            ChangeRootName(root);           
        }
        public void Write() { }
        public void Read() { }
        /// <summary>
        /// attribute params : declare or null . Must not blank !
        /// </summary>
        /// <param name="nodeName"></param>
        /// <param name="parentName"></param>
        /// <param name="parentAttribute"></param>
        /// <returns></returns>
        public XmlNode InsertNode(string nodeName, string parentName, params XAttribute[] parentAttribute) 
        {
            XmlDocument document = root.OwnerDocument;  
            XmlNodeList parents = GetNodes(parentName, parentAttribute);
            XmlElement node = document.CreateElement(nodeName);
            for (int i = 0; i < parents.Count; i++)
            {
                parents[i].AppendChild(node);
            }
            document.Save(path);
            return node;
        }
        public XmlNode InsertNode(string nodeName)
        {
            if (nodeName.Contains(' '))
            {
                return null;
            }
            XmlDocument document = root.OwnerDocument;
            XmlElement newNode = document.CreateElement(nodeName);
            root.AppendChild(newNode);
            document.Save(path);
            return newNode;
        }
        /// <summary>
        /// attribute params : declare or null . Must not blank !
        /// </summary>
        /// <param name="name"></param>
        /// <param name="attribute"></param>
        /// <returns></returns>
        public XmlNodeList GetNodes(string name, params XAttribute[] attribute)
        {
            XmlDocument document = root.OwnerDocument;
            string selectStr = "//" + name;

            if (name == null || name.Count() == 0)
            {
                //param : parentName is invalid
                selectStr = "//" + this.root.Name;
            }
            else
            {
                if (attribute != null )
                {
                    //param : parentAttribute is valid
                    selectStr += "[";
                    for (int i = 0; i < attribute.Count(); i++)
                    {
                        selectStr += "@" + attribute[i].attr + " = '" + attribute[i].value + "' ";
                    }
                    selectStr += "]";
                }
            }
            return document.SelectNodes(selectStr);
        }
        public bool Delete(string name, params XAttribute[] attribute)
        {
            bool ret = false;
            XmlDocument document = root.OwnerDocument;
            XmlNodeList nodes = GetNodes(name, attribute);
            for (int i = 0; i < nodes.Count; i++)
            {
                XmlNode node = nodes.Item(i);
                if (!node.Equals((XmlNode)root))
                {
                    XmlNode parent = node.ParentNode;
                    parent.RemoveChild(node);
                    document.Save(path);
                    ret = true;
                }
                else
                {
                    XmlDocument newDoc = new XmlDocument();
                    newDoc.Save(path);
                    ret = true;
                }
            }
            return ret;   
        }
    }

    public class XAttribute
    {
        public string attr;
        public string value;

        public XAttribute(string attr, string value)
        {
            this.attr = attr;
            this.value = value;
        }
    }
   
}
